home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Just Call Me Internet
/
Just Call Me Internet.iso
/
prog
/
atari
/
c
/
nos042_s
/
sz.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-09-16
|
48KB
|
2,130 lines
/****************************************************************************
* Language : Turbo C 2.0
* Logfile : sz.c
* Project : Comms library.
* Date : 24 Jan 90
* Revision : 1.1 GT PC version.
* 07 Mar 90 : 1.2 GT Background transfer.
* 25 Oct 92 : 1.3 GT Mods for KA9Q.
* 30 Jan 93 : 1.4 GT Fix KA9Q background working.
* 21 Feb 93 : 1.5 GT Fix reporting.
* 08 May 93 : 1.6 GT Fix warnings.
* 24 Jan 94 : 1.7 GT _flush -> _rbsb_flush.
*****************************************************************************
* Purpose : File send driver.
*****************************************************************************
* : This module is based on the equivalent module in the
* : 31 Aug 87 version of rz/sz.
* $Id: sz.c 1.5 94/01/26 13:55:15 ROOT_DOS Exp $
*
* ATARI Version by David Nash - dnash@chaos.demon.co.uk
*
****************************************************************************/
#include <io.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <setjmp.h>
#include <ctype.h>
#include <stdarg.h>
#include <time.h>
#include <process.h>
#include <string.h>
#include <io.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dos.h>
#ifdef ATARI
#include <ext.h>
#endif
#include "config.h"
#include "socket.h"
#include "proc.h"
/* kludge for krappy Microsoft */
#ifndef S_IFBLK
#define S_IFBLK 0x3000
#endif
#include "zmodem.h"
#include "sz.h"
#include "rbsb.h" /* most of the system dependent stuff here */
#include "zm.h"
#ifdef DEBUGZ
#include "tty.h"
#endif
#define HOWMANY 2048
#define NO_DCD 1
/****************************************************************************
* Local prototypes. *
****************************************************************************/
#if 0
static void bibi (int n);
#endif
static void chkinvok (char protocol);
static int readline (int n);
static void purgeline (void);
#if 0
static void onintr (void);
#endif
static int wcsend (int argc, char **argp);
static int wcs (char *oname);
static int wctxpn (char *name);
static int getnak (void);
static int wctx (long flen);
static int wcputsec (char *buf, int sectnum, int cseclen);
static int filbuf (char *buf, int count);
static int zfilbuf (char *buf, int count);
static int readock (int timeout, int count);
static int getzrxinit (void);
static int sendzsinit (void);
static int zsendfile (char *buf, int blen);
static int zsendfdata (void);
static int getinsync (int flag);
static void saybibi (void);
static int zsendcmd (char *buf, int blen);
/****************************************************************************
* Global data. *
****************************************************************************/
int _Zmodem = 0; /* ZMODEM protocol requested */
unsigned _Baud_z = 19200;
static unsigned Txwindow; /* Control the size of the transmitted window */
static unsigned Txwspac; /* Spacing between zcrcq requests */
static unsigned Txwcnt; /* Counter used to space ack requests */
static long Lrxpos; /* Receiver's last reported offset */
int _errors;
int _sending; /* TRUE - sending a file. */
int _zperr_handle; /* -> log file */
/*
* Attention string to be executed by receiver to interrupt streaming data
* when an error is detected. A pause (0336) may be needed before the
* ^C (03) or after it.
*/
static char Myattn[] = { 0 };
static int in;
int _Lastrx; /* was char */
int _Crcflg; /* was char */
int _Wcsmask = 0377;
/* Command line option flags */
int _Verbose = 0;
static int Modem2 = 0; /* XMODEM Protocol - don't send pathnames */
int _Quiet = 0; /* overrides logic that would otherwise set
verbose */
static int Ascii = 0; /* Add CR's for brain damaged programs */
static int Fullname = 0; /* transmit full pathname */
static int Unlinkafter = 0; /* Unlink file after it is sent */
static int Dottoslash = 0; /* Change foo.bar.baz to foo/bar/baz */
int _firstsec;
static int errcnt = 0; /* number of files unreadable */
static int blklen = SECSIZ; /* length of transmitted records */
static int Optiong; /* Let it rip no wait for sector ACK's */
static int Noeofseen;
static int Totsecs; /* total number of sectors this file */
static char txbuf[KSIZE];
static int Filcnt = 0; /* count of number of files opened */
static int Lfseen = 0;
static unsigned Rxbuflen = 16384; /* Receiver's max buffer length */
static int Tframlen = 0; /* Override for tx frame length */
static int blkopt = 0; /* Override value for zmodem blklen */
static int Rxflags = 0;
static long bytcnt;
static int Wantfcs32 = TRUE; /* want to send 32 bit FCS */
static char Lzconv; /* Local ZMODEM file conversion request */
char _Lzmanag; /* Local ZMODEM file management request */
static int Lskipnocor;
static char Lztrans;
char _zconv; /* ZMODEM file conversion request */
char _zmanag; /* ZMODEM file management request */
char _ztrans; /* ZMODEM file transport request */
static int Command; /* Send a command, then exit. */
static char Cmdstr[256]; /* Command string */
static int Cmdtries = 11;
static int Cmdack1; /* Rx ACKs command, then do it */
static int Exitcode;
static int Testattn; /* Force receiver to send Attn, etc with qbf. */
static char *qbf = "The quick brown fox jumped over the lazy dog's back "
"1234567890\r\n";
static long Lastread; /* Beginning offset of last buffer read */
static int Lastn; /* Count of last buffer read or -1 */
static int Dontread; /* Don't read the buffer, it's still there */
static long Lastsync; /* Last offset to which we got a ZRPOS */
static int Beenhereb4; /* How many times we've been ZRPOS'd same
place */
jmp_buf _tohere; /* For user abort. */
static jmp_buf intrjmp; /* For the interrupt on RX CAN. */
jmp_buf _nocarrier; /* For carrier dropped. */
int _Zctlesc; /* Encode control characters */
int _Nozmodem = 0; /* If invoked as "sb" */
int _Zrwindow = 1400; /* RX window size (controls garbage count) */
void (*_do_report) (int type, void *data) = _zperr;
/* -> progress report fn */
extern int Lleft; /* number of chars in read buffer */
extern int Readnum; /* how many bytes to read */
#if 0
/****************************************************************************
* bibi *
* Called by signal interrupt or terminate to clean things up. *
****************************************************************************/
static void bibi (int n)
{
n = n;
_canit ();
_zperr_ ("Interrupted\n");
longjmp (_tohere, -1);
} /* static void bibi (int n) */
/****************************************************************************
* onintr *
* Called when Zmodem gets an interrupt (^X). *
****************************************************************************/
static void onintr (void)
{
#if 0
signal (SIGINT, SIG_IGN);
#endif
longjmp (intrjmp, -1);
} /* static void onintr (void) */
#endif
/****************************************************************************
* _no_carrier *
* Tests to see if CD still present, exits if not. *
****************************************************************************/
void _no_carrier (void)
{
#if !defined (NO_DCD)
if ((modemstat () & 0x80) == 0)
{
_zperr_ ("No carrier\n");
longjmp (_nocarrier, -1);
}
#endif
} /* void _no_carrier (void) */
/****************************************************************************
* _sendline and _xsendline *
* Send a character to remote. *
****************************************************************************/
void _sendline (int c)
{
if (_sending)
c &= _Wcsmask;
_xsendline (c);
} /* void _sendline (int c) */
void _xsendline (int c)
{
#ifdef DEBUGZ
if (_Verbose > 6)
_vfile ("_xsendline: %x\n", c);
#endif
while (_send ((unsigned char *) &c, 1) != 1)
_no_carrier ();
} /* void _xsendline (int c) */
/****************************************************************************
* _flushmo *
* Flush the ouput channel. *
****************************************************************************/
void _flushmo (void)
{
_rbsb_flush ();
} /* void _flushmo (void) */
/****************************************************************************
* _sendfile *
* Send file(s). Returns 0 if successful. *
****************************************************************************/
int _sendfile (int s, char protocol, char *options, char *filenames[],
void (*reporter) (int type, void *data))
{
char *cp;
int npats;
int agcnt;
char **agcv;
char **patts;
int i; /* loop counter */
#if 0
void (*sigint_sav) (int);
void (*sigterm_sav) (int);
#endif
/* Initialise global variables */
_z_socket = s;
Lleft = 0;
Readnum = HOWMANY;
Ascii = 0;
blklen = SECSIZ;
blkopt = 0;
Cmdtries = 11;
Command = FALSE;
_do_report = _zperr;
Dottoslash = 0;
errcnt = 0;
Exitcode = 0;
Filcnt = 0;
Fullname = 0;
Lfseen = 0;
Modem2 = 0;
_Nozmodem = 0;
_Quiet = 0;
Rxbuflen = 16384;
Rxflags = 0;
_sending = TRUE;
Tframlen = 0;
Unlinkafter = 0;
Wantfcs32 = TRUE;
_Wcsmask = 0377;
_Verbose = 0;
_Zmodem = 0;
_Zrwindow = 1400;
/* See if null padding required */
if (((cp = getenv ("ZNULLS")) != NULL) && *cp)
_Znulls = atoi (cp);
chkinvok (protocol); /* select protocol */
_Rxtimeout = 600;
npats = 0;
/* Parse options. */
cp = options;
while (*cp != '\0')
{
switch (*cp++)
{
case '+': /* append file */
_Lzmanag = ZMAPND;
break;
case '7': /* strip top bit */
_Wcsmask = 0177;
break;
case 'a': /* NL -> CRNL conversion */
Lzconv = ZCNL;
Ascii = TRUE;
break;
case 'b': /* binary file */
Lzconv = ZCBIN;
break;
case 'i': /* command, no wait */
Cmdack1 = ZCACK1;
/* **** FALL THROUGH TO **** */
case 'c': /* command, wait */
if (*cp != '#') /* command delimiter? */
return (ERROR); /* invalid parameter */
Command = TRUE;
for (i = 0; i < sizeof (Cmdstr); i++)
{
if (*++cp == '#')
break; /* end of command */
Cmdstr[i] = *cp;
}
Cmdstr[i] = '\0';
cp++;
break;
case 'd': /* . -> / */
++Dottoslash;
/* **** FALL THROUGH TO **** */
case 'f': /* full pathnames */
Fullname = TRUE;
break;
case 'e': /* escape ctl chars */
_Zctlesc = 1;
break;
case 'k': /* YMODEM 1k */
blklen = KSIZE;
break;
case 'L': /* limit subpacket length */
sscanf (cp, "%d", &blkopt);
if (blkopt < 24 || blkopt > 1024)
return (ERROR); /* bad length */
cp = _stbnb (cp); /* skip whitespace */
break;
case 'l': /* limit frame length */
sscanf (cp, "%d", &Tframlen);
if (Tframlen < 32 || Tframlen > 1024)
return (ERROR);
cp = _stbnb (cp);
break;
case 'N': /* source newer or longer */
_Lzmanag = ZMNEWL;
break;
case 'n': /* source newer */
_Lzmanag = ZMNEW;
break;
case 'o': /* 16 bit CRC */
Wantfcs32 = FALSE;
break;
case 'p': /* no target overwrite */
_Lzmanag = ZMPROT;
break;
case 'r': /* resume file transfer */
Lzconv = ZCRESUM;
break;
case 'q': /* quiet */
_Quiet = TRUE;
_Verbose = 0;
break;
case 't': /* change timeout */
sscanf (cp, "%d", &_Rxtimeout);
if (_Rxtimeout < 10 || _Rxtimeout > 1000)
return (ERROR);
cp = _stbnb (cp);
break;
case 'T': /* test attention mode */
Testattn = TRUE;
break;
case 'u': /* erase after send */
++Unlinkafter;
break;
case 'v': /* debugging info */
++_Verbose;
break;
case 'w': /* window size */
sscanf (cp, "%d", &Txwindow);
if (Txwindow < 256)
Txwindow = 256;
Txwindow = (Txwindow / 64) * 64;
Txwspac = Txwindow / 4;
if (blkopt > Txwspac || (!blkopt && Txwspac < 1024))
blkopt = Txwspac;
cp = _stbnb (cp);
break;
case 'X': /* use XMODEM */
++Modem2;
break;
case 'Y': /* overwrite target only if
present */
Lskipnocor = TRUE;
/* **** FALL THROUGH TO **** */
case 'y': /* overwrite target always */
_Lzmanag = ZMCLOB;
break;
default:
return (ERROR);
} /* switch (*cp++) */
} /* while (*cp != '\0') */
/* parse filenames */
patts = filenames;
while (**patts != '\0')
{
npats++; /* bump filename count */
patts++; /* try next string */
}
patts = filenames;
if (npats < 1 && !Command)
return (ERROR); /* no filenames */
/* Get reporter function */
if (reporter != NULL)
_do_report = reporter; /* user supplied fn */
#if 0
if (_Verbose)
{
if ((_zperr_handle = open (LOGFILE,
O_CREAT | O_APPEND | O_WRONLY | O_TEXT,
S_IFREG | S_IWRITE)) == -1)
{
char buff[80];
sprintf (buff, "Can't open log file %s: %s\n",
LOGFILE, strerror (errno));
(*_do_report) (2, buff);
(*_do_report) (3, &Filcnt); /* report no of files */
return (ERROR);
}
_do_report = _zperr; /* use default reporter */
}
#endif
if (!_Quiet)
{
if (_Verbose == 0)
_Verbose = 2;
}
#if 0
if ((sigint_sav = signal (SIGINT, bibi)) == SIG_IGN)
signal (SIGINT, SIG_IGN);
else
signal (SIGINT, bibi);
#endif
#if 0
sigint_sav = signal (SIGINT, bibi);
sigterm_sav = signal (SIGTERM, bibi);
#endif
/* Organise escape routes. */
if (setjmp (_tohere) != 0)
{
(*_do_report) (3, &Filcnt); /* report no of files */
#if 0
signal (SIGINT, sigint_sav);
signal (SIGTERM, sigterm_sav);
(void) close (_zperr_handle);
#endif
return (-2); /* user abort */
}
if (setjmp (_nocarrier) != 0)
{
(*_do_report) (3, &Filcnt); /* report no of files */
#if 0
signal (SIGINT, sigint_sav);
signal (SIGTERM, sigterm_sav);
(void) close (_zperr_handle);
#endif
return (-3); /* lost carrier */
}
if (!Modem2)
{
#if 0
if (!_Nozmodem)
{
printf ("rz\r");
}
#endif
if (!Command && !_Quiet && _Verbose != 1)
{
_say ("%d file%s requested:\r\n",
npats, npats > 1 ? "s" : "");
for (agcnt = npats, agcv = patts; --agcnt >= 0;)
{
_say ("%s ", *agcv++);
}
_say ("\r\n");
_say ("\r\n\bSending in Batch Mode\r\n");
}
if (!_Nozmodem)
{
_stohdr (0L);
if (Command)
_Txhdr[ZF0] = ZCOMMAND;
_zshhdr (ZRQINIT, _Txhdr);
}
} /* if (!Modem2) */
if (Command)
{
if (getzrxinit ())
{
Exitcode = -2;
_canit ();
}
else if (zsendcmd (Cmdstr, 1 + strlen (Cmdstr)))
{
Exitcode = -2;
_canit ();
}
}
else if (wcsend (npats, patts) == ERROR)
{
Exitcode = -2;
_canit ();
}
if (!_Quiet)
_say ("\n");
(*_do_report) (3, &Filcnt); /* report no of files */
#if 0
signal (SIGINT, sigint_sav);
signal (SIGTERM, sigterm_sav);
(void) close (_zperr_handle);
#endif
return (Exitcode);
} /* int _sendfile (int s, char protocol, char *options,
char *filenames[],
void (*reporter) (int type, void *data)) */
/****************************************************************************
* _stbnb *
* Skip to blank then skip to nonblank. *
****************************************************************************/
char *_stbnb (char *cp)
{
/* skip to blank */
while (!isspace (*cp))
cp++;
/* skip to nonblank */
while (isspace (*cp))
cp++;
return (cp);
} /* char *_stbnb (char *cp) */
/****************************************************************************
* wcsend *
* Send <argc> strings (<argp>) to remote. *
****************************************************************************/
static int wcsend (int argc, char **argp)
{
int n;
_Crcflg = FALSE;
_firstsec = TRUE;
bytcnt = -1;
for (n = 0; n < argc; ++n)
{
Totsecs = 0;
if (wcs (argp[n]) == ERROR)
return ERROR;
}
Totsecs = 0;
if (Filcnt == 0)
{
/* bitch if we couldn't open ANY files */
#if 0
if (1)
#endif
{
Command = TRUE;
strcpy (Cmdstr, "echo Can't open any requested files");
if (getnak ())
{
Exitcode = -2;
_canit ();
}
if (!_Zmodem)
_canit ();
else if (zsendcmd (Cmdstr, 1 + strlen (Cmdstr)))
{
Exitcode = -2;
_canit ();
}
Exitcode = -2;
return OK;
}
#if 0 /* unreachable */
_canit ();
_say ("\r\nCan't open any requested files.\r\n");
return ERROR;
#endif
} /* if (Filcnt == 0) */
if (_Zmodem)
saybibi ();
else if (!Modem2)
wctxpn ("");
return OK;
} /* static int wcsend (int argc, char **argp) */
/****************************************************************************
* wcs *
* Send file <oname> to remote. *
****************************************************************************/
static int wcs (char *oname)
{
int c;
struct stat f;
char name[PATHLEN];
strcpy (name, oname);
if ((in = open (oname, O_RDONLY | O_BINARY, S_IREAD)) == -1)
{
if (_Verbose > 2)
{
_say ("Couldn't open %s\r\n", oname);
}
++errcnt;
return OK; /* pass over it, there may be others */
}
++Noeofseen;
Lastread = 0;
Lastn = -1;
Dontread = FALSE;
/* Check for directory or block special files */
fstat (in, &f);
c = f.st_mode & S_IFMT;
if (c == S_IFDIR || c == S_IFBLK)
{
close (in);
return OK;
}
++Filcnt;
switch (wctxpn (name))
{
case ERROR:
return ERROR;
case ZSKIP:
return OK;
}
if (!_Zmodem && wctx (f.st_size) == ERROR)
return ERROR;
if (Unlinkafter)
unlink (oname);
return 0;
} /* static int wcs (char *oname) */
/****************************************************************************
* wctxpn *
* Generate and transmit pathname block consisting of pathname (null *
* terminated), file length, mode time and file mode in octal as provided *
* by the fstat call. *
* N.B.: modifies the passed name, may extend it! *
****************************************************************************/
static int wctxpn (char *name)
{
char *p, *q;
char name2[PATHLEN];
struct stat f;
(*_do_report) (0, name);
if (Modem2)
{
if (*name && (fstat (in, &f) != -1))
{
_say ("Sending %s, %ld blocks: ", name, f.st_size >> 7);
}
_say ("Give your local XMODEM receive command now.\r\n");
return OK;
}
_vfile ("Awaiting pathname nak for %s", *name ? name : "<END>");
if (!_Zmodem)
if (getnak ())
return ERROR;
q = (char *) 0;
if (Dottoslash)
{
/* change . to . */
for (p = name; *p; ++p)
{
if (*p == '/')
q = p;
else if (*p == '.')
*(q = p) = '/';
}
if (q && strlen (++q) > 8)
{
/* If name > 8 chars */
q += 8; /* make it .ext */
strcpy (name2, q); /* save excess of name */
*q = '.';
strcpy (++q, name2); /* add it back */
}
} /* if (Dottoslash) */
for (p = name, q = txbuf; *p;)
if ((*q++ = *p++) == '\\' && !Fullname)
q = txbuf;
*q++ = 0;
p = q;
while (q < (txbuf + KSIZE))
*q++ = 0;
if (!Ascii && *name && fstat (in, &f) != -1)
sprintf (p, "%lu %lo %o", f.st_size, f.st_mtime, f.st_mode);
/* force 1k blocks if name won't fit in 128 byte block */
if (txbuf[125])
blklen = KSIZE;
else
{
/* A little goodie for IMP/KMD */
if (_Zmodem)
blklen = SECSIZ;
txbuf[127] = (char) ((f.st_size + 127) >> 7);
txbuf[126] = (char) ((f.st_size + 127) >> 15);
}
if (_Zmodem)
return zsendfile (txbuf, (int) (1 + strlen (p) + (p - txbuf)));
if (wcputsec (txbuf, 0, SECSIZ) == ERROR)
return ERROR;
return OK;
} /* static int wctxpn (char *name) */
/****************************************************************************
* getnak *
* Returns FALSE if NAK or ZPAD received. *
****************************************************************************/
static int getnak (void)
{
int firstch;
_Lastrx = 0;
for (;;)
{
switch (firstch = readock (800, 1))
{
case ZPAD:
if (getzrxinit ())
return ERROR;
Ascii = 0;
return FALSE;
case ZTIMEOUT:
_zperr_ ("Timeout on pathname");
return TRUE;
case WANTG:
Optiong = TRUE;
blklen=KSIZE;
case WANTCRC:
_Crcflg = TRUE;
case NAK:
return FALSE;
case CAN:
if ((firstch = readock (20, 1)) == CAN && _Lastrx == CAN)
return TRUE;
default:
break;
} /* switch (firstch = readock (800, 1)) */
_Lastrx = firstch;
} /* for (;;) */
} /* static int getnak (void) */
/****************************************************************************
* wctx *
* Send <flen> bytes (in blocks) using XMODEM or YMODEM. *
****************************************************************************/
static int wctx (long flen)
{
int thisblklen;
int sectnum, attempts, firstch;
long charssent;
charssent = 0;
_firstsec = TRUE;
thisblklen = blklen;
_vfile ("wctx:file length = %ld", flen);
while ((firstch = readock (_Rxtimeout, 2)) != NAK &&
firstch != WANTCRC &&
firstch != WANTG &&
firstch != ZTIMEOUT &&
firstch != CAN)
;
if (firstch == CAN)
{
_zperr_ ("Receiver CANcelled");
return ERROR;
}
if (firstch == WANTCRC)
_Crcflg = TRUE;
if (firstch == WANTG)
_Crcflg = TRUE;
sectnum = 0;
for (;;)
{
if (flen <= (charssent + 896L))
thisblklen = 128;
if (!filbuf (txbuf, thisblklen))
break;
if (wcputsec (txbuf, ++sectnum, thisblklen) == ERROR)
return ERROR;
charssent += thisblklen;
}
close (in);
attempts = 0;
do
{
purgeline ();
_sendline (EOT);
++attempts;
firstch = (readock (_Rxtimeout, 1));
} while (firstch != ACK && attempts < RETRYMAX);
if (attempts == RETRYMAX)
{
_zperr_ ("No ACK on EOT");
return ERROR;
}
else
return OK;
} /* static int wctx (long flen) */
/****************************************************************************
* wcputsec *
* Send block <sectnum> of length <cseclen> using XMODEM or YMODEM. *
****************************************************************************/
static int wcputsec (char *buf, int sectnum, int cseclen)
{
int checksum, wcj;
char *cp;
unsigned oldcrc;
int firstch;
int attempts;
firstch = 0; /* part of logic to detect CAN CAN */
_vfile ("Sector %3d %2dk\n", Totsecs, Totsecs / 8 );
if (_Verbose > 1)
_say ("\rSector %3d %2dk ", Totsecs, Totsecs / 8 );
for (attempts = 0; attempts <= RETRYMAX; attempts++)
{
_Lastrx = firstch;
_sendline (cseclen == KSIZE ? STX : SOH);
_sendline (sectnum);
_sendline (- sectnum - 1);
oldcrc = checksum = 0;
for (wcj = cseclen, cp = buf; --wcj >= 0;)
{
_sendline (*cp);
oldcrc = updcrc ((0377 & *cp), oldcrc);
checksum += *cp++;
}
if (_Crcflg)
{
oldcrc = updcrc (0, updcrc (0, oldcrc));
_sendline ((int) oldcrc >> 8);
_sendline ((int) oldcrc);
}
else
_sendline (checksum);
if (Optiong)
{
_firstsec = FALSE;
return OK;
}
firstch = readock (_Rxtimeout, (Noeofseen && sectnum) ? 2 : 1);
gotnak:
switch (firstch)
{
case CAN:
if (_Lastrx == CAN)
{
cancan:
_zperr_ ("Cancelled");
return ERROR;
}
break;
case ZTIMEOUT:
_zperr_ ("Timeout on sector ACK");
continue;
case WANTCRC:
if (_firstsec)
_Crcflg = TRUE;
case NAK:
_zperr_ ("NAK on sector");
continue;
case ACK:
_firstsec = FALSE;
Totsecs += (cseclen >> 7);
return OK;
case ERROR:
_zperr_ ("Got burst for sector ACK");
break;
default:
_zperr_ ("Got %02x for sector ACK", firstch);
break;
} /* switch (firstch) */
for (;;)
{
_Lastrx = firstch;
if ((firstch = readock (_Rxtimeout, 2)) == ZTIMEOUT)
break;
if (firstch == NAK || firstch == WANTCRC)
goto gotnak;
if (firstch == CAN && _Lastrx == CAN)
goto cancan;
}
} /* for (attempts = 0; attempts <= RETRYMAX; attempts++) */
_zperr_ ("Retry Count Exceeded");
return ERROR;
} /* static int wcputsec (char *buf, int sectnum, int cseclen) */
/****************************************************************************
* filbuf *
* Fill <buf> with <count> chars padding with ^Z for CPM. *
****************************************************************************/
static int filbuf (char *buf, int count)
{
int c, m;
char locbuf[BUFSIZ]; /* local read buffer */
char *q; /* -> read buffer */
int rcount; /* no of chars read */
if (!Ascii)
{
m = read (in, buf, count);
if (m <= 0)
return 0;
while (m < count)
buf[m++] = 032;
return count;
}
m = count;
if (Lfseen)
{
*buf++ = 012;
--m;
Lfseen = 0;
}
#if 0
while ((c = getc (in)) != EOF)
#endif
while ((rcount = read (in, locbuf, sizeof (locbuf))) != -1)
{
q = locbuf;
while (rcount-- != 0)
{
c = *q++;
if (c == 012)
{
*buf++ = 015;
if (--m == 0)
{
Lfseen = TRUE;
break;
}
}
*buf++ = (char) c;
if (--m == 0)
break;
} /* while (rcount-- != 0) */
if (m == 0)
break;
} /* while ((rcount = read (in, locbuf, sizeof (locbuf)) != -1) */
if (m == count)
return 0;
else
while (--m >= 0)
*buf++ = CPMEOF;
return count;
} /* static int filbuf (char *buf, int count) */
/****************************************************************************
* zfilbuf *
* Fill <buf> with <count> chars. *
****************************************************************************/
static int zfilbuf (char *buf, int count)
{
#ifndef ATARI
if (eof (in))
return (0);
#endif
return (read (in, buf, count));
#if 0
int c, m;
m = count;
while ((c = getc (in)) != EOF)
{
*buf++ = c;
if (--m == 0)
break;
}
return (count - m);
#endif
} /* static int zfilbuf (char *buf, int count) */
/****************************************************************************
* _vfile *
* Print debugging message. *
****************************************************************************/
void _vfile (char *f, ...)
{
va_list parms; /* -> variable arguments */
char buf[128]; /* for text */
va_start (parms, f);
if (_Verbose > 2)
{
vsprintf (buf, f, parms);
strcat (buf, "\n");
(*_do_report) (2, buf); /* call reporter function */
}
va_end (parms);
} /* void _vfile (char *f, ...) */
/****************************************************************************
* _say *
* Report something unconditionally. *
****************************************************************************/
void _say (char *f, ...)
{
va_list parms; /* -> variable arguments */
char buf[128]; /* for text */
va_start (parms, f);
vsprintf (buf, f, parms);
(*_do_report) (2, buf); /* call reporter function */
va_end (parms);
} /* void say (char *f, ...) */
/****************************************************************************
* readock *
* Reads character(s) from receive channel. It attempts to read count *
* characters (1 <= <count> <= 3). If it gets more than one, it is an *
* error unless all are CAN (otherwise, only normal response is ACK, CAN *
* or C). Only looks for one if Optiong, which signifies cbreak, not raw *
* input. <timeout> is in tenths of seconds. *
****************************************************************************/
static int readock (int timeout, int count)
{
int c, t;
static unsigned char byt[5];
time_t start; /* start time for timeout */
if (Optiong)
count = 1; /* Special hack for cbreak */
t = timeout / 10;
if (t < 2)
t = 2;
time (&start);
if (_Verbose > 5)
{
_say ("Timeout=%d Calling alarm (%d) ", timeout, t);
byt[1] = 0;
}
_no_carrier ();
while (1)
{
c = _receive (byt, count);
if (c != 0)
break; /* got something */
if (time (NULL) >= start + t)
{
_zperr_ ("TIMEOUT");
return ZTIMEOUT;
}
}
if (_Verbose > 5)
_say ("ret cnt=%d %x %x\n", c, byt[0], byt[1]);
if (c < 1)
return ZTIMEOUT;
if (c == 1)
return (byt[0] & 0377);
else
while (c)
if (byt[--c] != CAN)
return ERROR;
return CAN;
} /* static int readock (int timeout, int count) */
/****************************************************************************
* readline *
* Receive one control character with timeout. *
****************************************************************************/
static int readline (int n)
{
return (readock (n, 1));
} /* static int readline (int n) */
/****************************************************************************
* purgeline *
* Flush input buffer. *
****************************************************************************/
static void purgeline (void)
{
unsigned char c;
while (_rdchk () > 0)
_receive (&c, 1);
#if 0
while (_receive (&c, 1) > 0)
;
#endif
} /* static void purgeline (void) */
/****************************************************************************
* _canit *
* Send cancel string to get the other end to shut up. *
****************************************************************************/
void _canit (void)
{
int result;
int count = 0;
#ifdef DEBUGZ
char buf[128];
#endif
static unsigned char canistr[] = { 24, 24, 24, 24, 24, 24, 24, 24, 24, 24,
8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 0 };
_flushmo ();
while (count != strlen ((char *) canistr))
{
while ((result = _send (canistr + count,
strlen ((char *) canistr) - count)) == 0)
;
count += result;
#ifdef DEBUGZ
sprintf (buf, "_canit: count = %d\r\n", count);
_tout (buf);
#endif
}
_flushmo ();
} /* void _canit (void) */
/****************************************************************************
* _zperr_ *
* Log an error. *
****************************************************************************/
void _zperr_ (char *s, ...)
{
va_list parms;
char buf0[128]; /* for report string */
char buf1[128];
char buf2[128];
if (_Verbose <= 0)
return;
va_start (parms, s);
sprintf (buf1, "Retry %d: ", _errors);
vsprintf (buf2, s, parms);
sprintf (buf0, "%s%s\n", buf1, buf2);
(*_do_report) (2, buf0); /* call reporter function */
va_end (parms);
} /* void _zperr_ (char *s, ...) */
/****************************************************************************
* _zperr *
* Default progress report function. *
****************************************************************************/
void _zperr (int type, void *data)
{
switch (type)
{
case 0: /* filename */
tprintf ("FILE: %s\n", data);
break;
case 1: /* bytes transferred */
tprintf ("\r%7ld ", *((long *) data));
break;
case 2: /* other text */
tprintf ("%s", data);
break;
case 3: /* end of transfer */
tprintf ("%5d files transferred\n", *((int *) data));
break;
default:
return;
} /* switch (type) */
} /* void _zperr (int type, void *data) */
/****************************************************************************
* _substr *
* Searches for <token> in string <s>. Returns pointer to token within *
* string if found, NULL otherwise. *
****************************************************************************/
char *_substr (char *s, char *t)
{
char *ss,*tt;
/* search for first char of token */
for (ss = s; *s; s++)
{
if (*s == *t)
{
/* compare token with substring */
for (ss = s, tt = t;;)
{
if (*tt == 0)
return s;
if (*ss++ != *tt++)
break;
}
} /* if (*s == *t) */
} /* for (ss = s; *s; s++) */
return NULL;
} /* char *_substr (char *s, char *t) */
/****************************************************************************
* getzrxinit *
* Get the receiver's init parameters. *
****************************************************************************/
static int getzrxinit (void)
{
int n;
struct stat f;
for (n = 10; --n >= 0;)
{
switch (_zgethdr (_Rxhdr, 1))
{
case ZCHALLENGE: /* Echo receiver's challenge numbr */
_stohdr (_Rxpos);
_zshhdr (ZACK, _Txhdr);
continue;
case ZCOMMAND: /* They didn't see out ZRQINIT */
_stohdr (0L);
_zshhdr (ZRQINIT, _Txhdr);
continue;
case ZRINIT:
Rxflags = 0377 & _Rxhdr[ZF0];
_Txfcs32 = (Wantfcs32 && (Rxflags & CANFC32));
_Zctlesc |= Rxflags & TESCCTL;
Rxbuflen = (0377 & _Rxhdr[ZP0]) + ((0377 & _Rxhdr[ZP1]) << 8);
_vfile ("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen);
#if 0
signal (SIGINT, SIG_IGN);
#endif
/* Override to force shorter frame length */
if (Rxbuflen && (Rxbuflen > Tframlen) && (Tframlen >= 32))
Rxbuflen = Tframlen;
if (!Rxbuflen && (Tframlen >= 32) && (Tframlen <= 1024))
Rxbuflen = Tframlen;
_vfile ("Rxbuflen=%d", Rxbuflen);
/*
* If input is not a regular file, force ACK's each 1024
* (A smarter strategey could be used here ...)
*/
fstat (in, &f);
if (((f.st_mode & S_IFMT) != S_IFREG) &&
(Rxbuflen == 0 || Rxbuflen > 1024))
Rxbuflen = 1024;
_vfile ("Rxbuflen=%d", Rxbuflen);
return (sendzsinit ());
case ZCAN:
case ZTIMEOUT:
return ERROR;
case ZRQINIT:
if (_Rxhdr[ZF0] == ZCOMMAND)
continue;
default:
_zshhdr (ZNAK, _Txhdr);
continue;
} /* switch (_zgethdr (_Rxhdr, 1)) */
} /* for (n = 10; --n >= 0;) */
return ERROR;
} /* static int getzrxinit (void) */
/****************************************************************************
* sendzsinit *
* Send send-init information. *
****************************************************************************/
static int sendzsinit (void)
{
int c;
if (Myattn[0] == '\0' && (!_Zctlesc || (Rxflags & TESCCTL)))
return OK;
_errors = 0;
for (;;)
{
_stohdr (0L);
if (_Zctlesc)
{
_Txhdr[ZF0] |= TESCCTL;
_zshhdr (ZSINIT, _Txhdr);
}
else
_zsbhdr (ZSINIT, _Txhdr);
_zsdata (Myattn, 1 + strlen (Myattn), ZCRCW);
c = _zgethdr (_Rxhdr, 1);
switch (c)
{
case ZCAN:
return ERROR;
case ZACK:
return OK;
default:
if (++_errors > 19)
return ERROR;
continue;
} /* switch (c) */
} /* for (;;) */
} /* static int sendzsinit (void) */
/****************************************************************************
* zsendfile *
* Send file name and related info. *
****************************************************************************/
static int zsendfile (char *buf, int blen)
{
int c;
for (;;)
{
_Txhdr[ZF0] = Lzconv; /* file conversion request */
_Txhdr[ZF1] = _Lzmanag; /* file management request */
if (Lskipnocor)
_Txhdr[ZF1] |= ZMSKNOLOC;
_Txhdr[ZF2] = Lztrans; /* file transport request */
_Txhdr[ZF3] = 0;
_zsbhdr (ZFILE, _Txhdr);
_zsdata (buf, blen, ZCRCW);
again:
c = _zgethdr (_Rxhdr, 1);
switch (c)
{
case ZRINIT:
while ((c = readline (50)) > 0)
if (c == ZPAD)
{
goto again;
}
/* **** FALL THRU TO **** */
default:
continue;
case ZCAN:
case ZTIMEOUT:
case ZABORT:
case ZFIN:
return ERROR;
case ZSKIP:
close (in);
return c;
case ZRPOS:
/*
* Suppress zcrcw request otherwise triggered by
* lastyunc==bytcnt
*/
Lastsync = (bytcnt = _Txpos = _Rxpos) - 1;
lseek (in, _Rxpos, 0);
Dontread = FALSE;
return zsendfdata ();
} /* switch (c) */
} /* for (;;) */
} /* static int zsendfile (char *buf, int blen) */
/****************************************************************************
* zsendfdata *
* Send the data in the file. *
****************************************************************************/
static int zsendfdata (void)
{
int c, e, n;
int newcnt;
long tcount = 0;
int junkcount; /* Counts garbage chars received by TX */
static int tleft = 6; /* Counter for test mode */
int sent_zm = FALSE; /* TRUE - sent ZMODEM string */
if (_Baud_z > 300)
blklen = 256;
if (_Baud_z > 1200)
blklen = 512;
if (_Baud_z > 2400)
blklen = KSIZE;
if (Rxbuflen && blklen > Rxbuflen)
blklen = Rxbuflen;
if (blkopt && blklen > blkopt)
blklen = blkopt;
_vfile ("Rxbuflen=%d blklen=%d", Rxbuflen, blklen);
_vfile ("Txwindow = %u Txwspac = %d", Txwindow, Txwspac);
Lrxpos = 0;
junkcount = 0;
Beenhereb4 = FALSE;
somemore:
if (setjmp (intrjmp))
{
waitack:
junkcount = 0;
c = getinsync (0);
gotack:
switch (c)
{
default:
case ZCAN:
close (in);
return ERROR;
case ZSKIP:
close (in);
return c;
case ZACK:
case ZRPOS:
break;
case ZRINIT:
return OK;
} /* switch (c) */
/*
* If the reverse channel can be tested for data,
* this logic may be used to detect error packets
* sent by the receiver, in place of setjmp/longjmp
* _rdchk () returns non 0 if a character is available
*/
while (_rdchk ())
{
switch (readline (1))
{
case CAN:
case ZPAD:
c = getinsync (1);
goto gotack;
case XOFF: /* Wait a while for an XON */
case XOFF | 0200:
readline(100);
} /* switch (readline (1)) */
} /* while (_rdchk ()) */
} /* if (setjmp (intrjmp)) */
#if 0
signal (SIGINT, onintr);
#endif
newcnt = Rxbuflen;
Txwcnt = 0;
_stohdr (_Txpos);
_zsbhdr (ZDATA, _Txhdr);
/*
* Special testing mode. This should force receiver to Attn,ZRPOS
* many times. Each time the signal should be caught, causing the
* file to be started over from the beginning.
*/
if (Testattn)
{
if (--tleft)
while (tcount < 20000)
{
_send ((unsigned char *) qbf, strlen (qbf));
tcount += strlen (qbf);
while (_rdchk ())
{
switch (readline (1))
{
case CAN:
case ZPAD:
goto waitack;
case XOFF: /* Wait for XON */
case XOFF | 0200:
readline (100);
} /* switch (readline (1)) */
} /* while (_rdchk ()) */
} /* while (tcount < 20000) */
#if 0
signal (SIGINT, SIG_IGN);
#endif
_canit ();
sleep (3);
purgeline ();
printf ("\nTcount = %ld\n", tcount);
if (tleft)
{
printf ("ERROR: Interrupts Not Caught\n");
return (ERROR);
}
return (0);
} /* if (Testattn) */
do
{
if (Dontread)
{
n = Lastn;
}
else
{
n = zfilbuf (txbuf, blklen);
Lastread = _Txpos;
Lastn = n;
}
Dontread = FALSE;
if (n < blklen)
e = ZCRCE;
else if (junkcount > 3)
e = ZCRCW;
else if (bytcnt == Lastsync)
e = ZCRCW;
else if (Rxbuflen && (newcnt -= n) <= 0)
e = ZCRCW;
else if (Txwindow && (Txwcnt += n) >= Txwspac)
{
Txwcnt = 0;
e = ZCRCQ;
}
else
e = ZCRCG;
if (_Verbose > 1)
{
(*_do_report) (1, &_Txpos);
if (!sent_zm)
{
sent_zm = TRUE;
_say ("ZMODEM%s", _Crc32t ? " CRC-32" : "");
}
}
_zsdata (txbuf, n, e);
bytcnt = _Txpos += n;
if (e == ZCRCW)
goto waitack;
/*
* If the reverse channel can be tested for data,
* this logic may be used to detect error packets
* sent by the receiver, in place of setjmp/longjmp
* _rdchk () returns non 0 if a character is available
*/
while (_rdchk ())
{
switch (readline (1))
{
case CAN:
case ZPAD:
c = getinsync (1);
if (c == ZACK)
break;
/* zcrce - dinna wanna starta ping-pong game */
_zsdata (txbuf, 0, ZCRCE);
goto gotack;
case XOFF: /* Wait a while for an XON */
case XOFF | 0200:
readline (100);
default:
++junkcount;
} /* switch (readline (1)) */
} /* while (_rdchk ()) */
if (Txwindow)
{
while ((tcount = _Txpos - Lrxpos) >= Txwindow)
{
_vfile ("%ld window >= %u", tcount, Txwindow);
if (e != ZCRCQ)
_zsdata (txbuf, 0, e = ZCRCQ);
c = getinsync (1);
if (c != ZACK)
{
_zsdata (txbuf, 0, ZCRCE);
goto gotack;
}
}
_vfile ("window = %ld", tcount);
}
} while (n == blklen);
#if 0
signal (SIGINT, SIG_IGN);
#endif
for (;;)
{
_stohdr (_Txpos);
_zsbhdr (ZEOF, _Txhdr);
switch (getinsync (0))
{
case ZACK:
continue;
case ZRPOS:
goto somemore;
case ZRINIT:
return OK;
case ZSKIP:
close (in);
return c;
default:
close (in);
return ERROR;
} /* switch (getinsync (0)) */
} /* for (;;) */
} /* static int zsendfdata (void) */
/****************************************************************************
* getinsync *
* Respond to receiver's complaint, get back in sync with receiver. *
****************************************************************************/
static int getinsync (int flag)
{
int c;
for (;;)
{
if (Testattn)
{
printf ("\r\n\n\n***** Signal Caught *****\r\n");
_Rxpos = 0;
c = ZRPOS;
}
else
c = _zgethdr (_Rxhdr, 0);
switch (c)
{
case ZCAN:
case ZABORT:
case ZFIN:
case ZTIMEOUT:
return ERROR;
case ZRPOS:
/* ************************************* */
/* If sending to a modem buffer, you */
/* might send a break at this point to */
/* dump the modem's buffer. */
if (Lastn >= 0 && Lastread == _Rxpos)
{
Dontread = TRUE;
}
else
{
#if 0
clearerr (in); /* In case file EOF seen */
#endif
lseek (in, _Rxpos, 0);
}
bytcnt = Lrxpos = _Txpos = _Rxpos;
if (Lastsync == _Rxpos)
{
if (++Beenhereb4 > 4)
if (blklen > 256)
blklen /= 2;
}
Lastsync = _Rxpos;
return c;
case ZACK:
Lrxpos = _Rxpos;
if (flag || _Txpos == _Rxpos)
return ZACK;
continue;
case ZRINIT:
case ZSKIP:
close (in);
return c;
case ERROR:
default:
_zsbhdr (ZNAK, _Txhdr);
continue;
} /* switch (c) */
} /* for (;;) */
} /* static int getinsync (int flag) */
/****************************************************************************
* saybibi *
* Say "bibi" to the receiver, try to do it cleanly. *
****************************************************************************/
static void saybibi (void)
{
for (;;)
{
_stohdr (0L); /* CAF Was _zsbhdr - minor change */
_zshhdr (ZFIN, _Txhdr); /* to make debugging easier */
switch (_zgethdr (_Rxhdr, 0))
{
case ZFIN:
_sendline ('O');
_sendline ('O');
_flushmo ();
case ZCAN:
case ZTIMEOUT:
return;
}
}
} /* static void saybibi (void) */
/****************************************************************************
* _bttyout *
* Local screen character display function. *
****************************************************************************/
void _bttyout (int c)
{
char str[2]; /* for making string */
if (_Verbose)
{
str[0] = (char) c;
str[1] = '\0';
_say (str);
}
} /* void _bttyout (int c) */
/****************************************************************************
* zsendcmd *
* Send command and related info. *
****************************************************************************/
static int zsendcmd (char *buf, int blen)
{
int c;
long cmdnum;
cmdnum = getpid ();
_errors = 0;
for (;;)
{
_stohdr (cmdnum);
_Txhdr[ZF0] = (char) Cmdack1;
_zsbhdr (ZCOMMAND, _Txhdr);
_zsdata (buf, blen, ZCRCW);
listen:
_Rxtimeout = 100; /* Ten second wait for resp. */
c = _zgethdr (_Rxhdr, 1);
switch (c)
{
case ZRINIT:
goto listen; /* CAF 8-21-87 */
case ERROR:
case ZTIMEOUT:
if (++_errors > Cmdtries)
return ERROR;
continue;
case ZCAN:
case ZABORT:
case ZFIN:
case ZSKIP:
case ZRPOS:
return ERROR;
default:
if (++_errors > 20)
return ERROR;
continue;
case ZCOMPL:
Exitcode = (int) _Rxpos;
saybibi ();
return OK;
} /* switch (c) */
} /* for (;;) */
} /* static int zsendcmd (char *buf, int blen) */
/****************************************************************************
* chkinvok *
* Set chosen protocol. *
****************************************************************************/
static void chkinvok (char protocol)
{
if (protocol == 'y')
{
_Nozmodem = TRUE;
blklen = KSIZE;
}
if (protocol == 'x')
{
Modem2 = TRUE;
}
} /* static void chkinvok (char protocol) */
/* End of sz.c */